home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / BaseClasses / strmctl.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  13.3 KB  |  387 lines

  1. //------------------------------------------------------------------------------
  2. // File: StrmCtl.cpp
  3. //
  4. // Desc: DirectShow base classes.
  5. //
  6. // Copyright (c) 1996-2001 Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9.  
  10. #include <streams.h>
  11. #include <strmctl.h>
  12.  
  13. CBaseStreamControl::CBaseStreamControl()
  14. : m_StreamState(STREAM_FLOWING)
  15. , m_StreamStateOnStop(STREAM_FLOWING) // means no pending stop
  16. , m_tStartTime(MAX_TIME)
  17. , m_tStopTime(MAX_TIME)
  18. , m_dwStartCookie(0)
  19. , m_dwStopCookie(0)
  20. , m_pRefClock(NULL)
  21. , m_FilterState(State_Stopped)
  22. , m_bIsFlushing(FALSE)
  23. , m_bStopSendExtra(FALSE) {
  24. }
  25.  
  26. CBaseStreamControl::~CBaseStreamControl() {
  27.     // Make sure we release the clock.
  28.     SetSyncSource(NULL);
  29.     return;
  30. }
  31.  
  32.  
  33. STDMETHODIMP CBaseStreamControl::StopAt(const REFERENCE_TIME * ptStop, BOOL bSendExtra, DWORD dwCookie) {
  34.     CAutoLock lck(&m_CritSec);
  35.     m_bStopSendExtra = FALSE;   // reset
  36.     m_bStopExtraSent = FALSE;
  37.     if(ptStop) {
  38.         if(*ptStop == MAX_TIME) {
  39.             DbgLog((LOG_TRACE,2,TEXT("StopAt: Cancel stop")));
  40.             CancelStop();
  41.             // If there's now a command to start in the future, we assume
  42.             // they want to be stopped when the graph is first run
  43.             if(m_FilterState == State_Stopped && m_tStartTime < MAX_TIME) {
  44.                 m_StreamState = STREAM_DISCARDING;
  45.                 DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
  46.             }
  47.             return NOERROR;
  48.         }
  49.         DbgLog((LOG_TRACE,2,TEXT("StopAt: %dms extra=%d"),
  50.             (int)(*ptStop/10000), bSendExtra));
  51.         // if the first command is to stop in the future, then we assume they
  52.         // want to be started when the graph is first run
  53.         if(m_FilterState == State_Stopped && m_tStartTime > *ptStop) {
  54.             m_StreamState = STREAM_FLOWING;
  55.             DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
  56.         }
  57.         m_bStopSendExtra = bSendExtra;
  58.         m_tStopTime = *ptStop;
  59.         m_dwStopCookie = dwCookie;
  60.         m_StreamStateOnStop = STREAM_DISCARDING;
  61.     }
  62.     else {
  63.         DbgLog((LOG_TRACE,2,TEXT("StopAt: now")));
  64.         // sending an extra frame when told to stop now would mess people up
  65.         m_bStopSendExtra = FALSE;
  66.         m_tStopTime = MAX_TIME;
  67.         m_dwStopCookie = 0;
  68.         m_StreamState = STREAM_DISCARDING;
  69.         m_StreamStateOnStop = STREAM_FLOWING;   // no pending stop
  70.     }
  71.     // we might change our mind what to do with a sample we're blocking
  72.     m_StreamEvent.Set();
  73.     return NOERROR;
  74. }
  75.  
  76.  
  77. STDMETHODIMP CBaseStreamControl::StartAt
  78. ( const REFERENCE_TIME *ptStart, DWORD dwCookie ) {
  79.     CAutoLock lck(&m_CritSec);
  80.     if(ptStart) {
  81.         if(*ptStart == MAX_TIME) {
  82.             DbgLog((LOG_TRACE,2,TEXT("StartAt: Cancel start")));
  83.             CancelStart();
  84.             // If there's now a command to stop in the future, we assume
  85.             // they want to be started when the graph is first run
  86.             if(m_FilterState == State_Stopped && m_tStopTime < MAX_TIME) {
  87.                 DbgLog((LOG_TRACE,2,TEXT("graph will begin by FLOWING")));
  88.                 m_StreamState = STREAM_FLOWING;
  89.             }
  90.             return NOERROR;
  91.         }
  92.         DbgLog((LOG_TRACE,2,TEXT("StartAt: %dms"), (int)(*ptStart/10000)));
  93.         // if the first command is to start in the future, then we assume they
  94.         // want to be stopped when the graph is first run
  95.         if(m_FilterState == State_Stopped && m_tStopTime >= *ptStart) {
  96.             DbgLog((LOG_TRACE,2,TEXT("graph will begin by DISCARDING")));
  97.             m_StreamState = STREAM_DISCARDING;
  98.         }
  99.         m_tStartTime = *ptStart;
  100.         m_dwStartCookie = dwCookie;
  101.         // if (m_tStopTime == m_tStartTime) CancelStop();
  102.     }
  103.     else {
  104.         DbgLog((LOG_TRACE,2,TEXT("StartAt: now")));
  105.         m_tStartTime = MAX_TIME;
  106.         m_dwStartCookie = 0;
  107.         m_StreamState = STREAM_FLOWING;
  108.     }
  109.     // we might change our mind what to do with a sample we're blocking
  110.     m_StreamEvent.Set();
  111.     return NOERROR;
  112. }
  113.  
  114.  
  115. //  Retrieve information about current settings
  116. STDMETHODIMP CBaseStreamControl::GetInfo(AM_STREAM_INFO *pInfo) {
  117.     if(pInfo == NULL)
  118.         return E_POINTER;
  119.  
  120.     pInfo->tStart = m_tStartTime;
  121.     pInfo->tStop  = m_tStopTime;
  122.     pInfo->dwStartCookie = m_dwStartCookie;
  123.     pInfo->dwStopCookie  = m_dwStopCookie;
  124.     pInfo->dwFlags = m_bStopSendExtra ? AM_STREAM_INFO_STOP_SEND_EXTRA : 0;
  125.     pInfo->dwFlags |= m_tStartTime == MAX_TIME ? 0 : AM_STREAM_INFO_START_DEFINED;
  126.     pInfo->dwFlags |= m_tStopTime == MAX_TIME ? 0 : AM_STREAM_INFO_STOP_DEFINED;
  127.     switch(m_StreamState) {
  128.         default:
  129.             DbgBreak("Invalid stream state");
  130.         case STREAM_FLOWING:
  131.             break;
  132.         case STREAM_DISCARDING:
  133.             pInfo->dwFlags |= AM_STREAM_INFO_DISCARDING;
  134.             break;
  135.     }
  136.     return S_OK;
  137. }
  138.  
  139.  
  140. void CBaseStreamControl::ExecuteStop() {
  141.     ASSERT(CritCheckIn(&m_CritSec));
  142.     m_StreamState = m_StreamStateOnStop;
  143.     if(m_dwStopCookie && m_pSink) {
  144.         DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STOPPED (%d)"),
  145.             m_dwStopCookie));
  146.         m_pSink->Notify(EC_STREAM_CONTROL_STOPPED, (LONG_PTR)this, m_dwStopCookie);
  147.     }
  148.     CancelStop(); // This will do the tidy up
  149. }
  150.  
  151. void CBaseStreamControl::ExecuteStart() {
  152.     ASSERT(CritCheckIn(&m_CritSec));
  153.     m_StreamState = STREAM_FLOWING;
  154.     if(m_dwStartCookie) {
  155.         DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STARTED (%d)"),
  156.             m_dwStartCookie));
  157.         m_pSink->Notify(EC_STREAM_CONTROL_STARTED, (LONG_PTR)this, m_dwStartCookie);
  158.     }
  159.     CancelStart(); // This will do the tidy up
  160. }
  161.  
  162. void CBaseStreamControl::CancelStop() {
  163.     ASSERT(CritCheckIn(&m_CritSec));
  164.     m_tStopTime = MAX_TIME;
  165.     m_dwStopCookie = 0;
  166.     m_StreamStateOnStop = STREAM_FLOWING;
  167. }
  168.  
  169. void CBaseStreamControl::CancelStart() {
  170.     ASSERT(CritCheckIn(&m_CritSec));
  171.     m_tStartTime = MAX_TIME;
  172.     m_dwStartCookie = 0;
  173. }
  174.  
  175.  
  176. // This guy will return one of the three StreamControlState's.  Here's what the caller
  177. // should do for each one:
  178. //
  179. // STREAM_FLOWING:      Proceed as usual (render or pass the sample on)
  180. // STREAM_DISCARDING:   Calculate the time 'til *pSampleStart and wait that long
  181. //                      for the event handle (GetStreamEventHandle()).  If the
  182. //                      wait expires, throw the sample away.  If the event
  183. //          fires, call me back, I've changed my mind.
  184. //          I use pSampleStart (not Stop) so that live sources don't
  185. //          block for the duration of their samples, since the clock
  186. //          will always read approximately pSampleStart when called
  187.  
  188.  
  189. // All through this code, you'll notice the following rules:
  190. // - When start and stop time are the same, it's as if start was first
  191. // - An event is considered inside the sample when it's >= sample start time
  192. //   but < sample stop time
  193. // - if any part of the sample is supposed to be sent, we'll send the whole
  194. //   thing since we don't break it into smaller pieces
  195. // - If we skip over a start or stop without doing it, we still signal the event
  196. //   and reset ourselves in case somebody's waiting for the event, and to make
  197. //   sure we notice that the event is past and should be forgotten
  198. // Here are the 19 cases that have to be handled (x=start o=stop <-->=sample):
  199. //
  200. // 1.   xo<-->      start then stop
  201. // 2.   ox<-->      stop then start
  202. // 3.    x<o->      start
  203. // 4.    o<x->      stop then start
  204. // 5.    x<-->o     start
  205. // 6.    o<-->x     stop
  206. // 7.     <x->o     start
  207. // 8.     <o->x     no change
  208. // 9.     <xo>      start
  209. // 10.    <ox>      stop then start
  210. // 11.    <-->xo    no change
  211. // 12.    <-->ox    no change
  212. // 13.   x<-->      start
  213. // 14.    <x->      start
  214. // 15.    <-->x     no change
  215. // 16.   o<-->      stop
  216. // 17.    <o->      no change
  217. // 18.    <-->o     no change
  218. // 19.    <-->      no change
  219.  
  220.  
  221. enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckSampleTimes
  222. ( const REFERENCE_TIME * pSampleStart, const REFERENCE_TIME * pSampleStop ) {
  223.     CAutoLock lck(&m_CritSec);
  224.  
  225.     ASSERT(!m_bIsFlushing);
  226.     ASSERT(pSampleStart && pSampleStop);
  227.  
  228.     // Don't ask how I came up with the code below to handle all 19 cases...
  229.     if(m_tStopTime >= *pSampleStart) {
  230.         if(m_tStartTime >= *pSampleStop)
  231.             return m_StreamState;       // cases  8 11 12 15 17 18 19
  232.  
  233.         if(m_tStopTime < m_tStartTime)
  234.             ExecuteStop();          // case 10
  235.  
  236.         ExecuteStart();                         // cases 3 5 7 9 13 14
  237.         return m_StreamState;
  238.     }
  239.  
  240.     if(m_tStartTime >= *pSampleStop) {
  241.         ExecuteStop();                          // cases 6 16
  242.         return m_StreamState;
  243.     }
  244.  
  245.     if(m_tStartTime <= m_tStopTime) {
  246.         ExecuteStart();
  247.         ExecuteStop();
  248.     }
  249.     else {
  250.         ExecuteStop();
  251.         ExecuteStart();
  252.     }
  253.  
  254.     return m_StreamState;       // case 1, 2, or 4
  255. }
  256.  
  257.  
  258. enum CBaseStreamControl::StreamControlState CBaseStreamControl::CheckStreamState( IMediaSample * pSample ) {
  259.  
  260.     REFERENCE_TIME rtBufferStart, rtBufferStop;
  261.     const BOOL bNoBufferTimes =
  262.         pSample == NULL ||
  263.         FAILED(pSample->GetTime(&rtBufferStart, &rtBufferStop));
  264.  
  265.     StreamControlState state;
  266.     LONG lWait;
  267.  
  268.     do {
  269.         // something has to break out of the blocking
  270.         if(m_bIsFlushing || m_FilterState == State_Stopped)
  271.             return STREAM_DISCARDING;
  272.  
  273.         if(bNoBufferTimes) {
  274.             //  Can't do anything until we get a time stamp
  275.             state = m_StreamState;
  276.             break;
  277.         }
  278.         else {
  279.             state = CheckSampleTimes(&rtBufferStart, &rtBufferStop);
  280.             if(state == STREAM_FLOWING)
  281.                 break;
  282.  
  283.             // we aren't supposed to send this, but we've been
  284.             // told to send one more than we were supposed to
  285.             // (and the stop isn't still pending and we're streaming)
  286.             if(m_bStopSendExtra && !m_bStopExtraSent &&
  287.                 m_tStopTime == MAX_TIME &&
  288.                 m_FilterState != State_Stopped) {
  289.                 m_bStopExtraSent = TRUE;
  290.                 DbgLog((LOG_TRACE,2,TEXT("%d sending an EXTRA frame"),
  291.                     m_dwStopCookie));
  292.                 state = STREAM_FLOWING;
  293.                 break;
  294.             }
  295.         }
  296.  
  297.         // We're in discarding mode
  298.  
  299.         // If we've no clock, discard as fast as we can
  300.         if(!m_pRefClock) {
  301.             break;
  302.  
  303.             // If we're paused, we can't discard in a timely manner because
  304.             // there's no such thing as stream times.  We must block until
  305.             // we run or stop, or we'll end up throwing the whole stream away
  306.             // as quickly as possible
  307.         }
  308.         else if(m_FilterState == State_Paused) {
  309.             lWait = INFINITE;
  310.  
  311.         }
  312.         else {
  313.             // wait until it's time for the sample until we say "discard"
  314.             // ("discard in a timely fashion")
  315.             REFERENCE_TIME rtNow;
  316.             EXECUTE_ASSERT(SUCCEEDED(m_pRefClock->GetTime(&rtNow)));
  317.             rtNow -= m_tRunStart;   // Into relative ref-time
  318.             lWait = LONG((rtBufferStart - rtNow)/10000); // 100ns -> ms
  319.             if(lWait < 10) break; // Not worth waiting - discard early
  320.         }
  321.  
  322.     } while(WaitForSingleObject(GetStreamEventHandle(), lWait) != WAIT_TIMEOUT);
  323.  
  324.     return state;
  325. }
  326.  
  327.  
  328. void CBaseStreamControl::NotifyFilterState( FILTER_STATE new_state, REFERENCE_TIME tStart ) {
  329.     CAutoLock lck(&m_CritSec);
  330.  
  331.     // or we will get confused
  332.     if(m_FilterState == new_state)
  333.         return;
  334.  
  335.     switch(new_state) {
  336.         case State_Stopped:
  337.  
  338.             DbgLog((LOG_TRACE,2,TEXT("Filter is STOPPED")));
  339.  
  340.             // execute any pending starts and stops in the right order,
  341.             // to make sure all notifications get sent, and we end up
  342.             // in the right state to begin next time (??? why not?)
  343.  
  344.             if(m_tStartTime != MAX_TIME && m_tStopTime == MAX_TIME) {
  345.                 ExecuteStart();
  346.             }
  347.             else if(m_tStopTime != MAX_TIME && m_tStartTime == MAX_TIME) {
  348.                     ExecuteStop();
  349.             }
  350.             else if(m_tStopTime != MAX_TIME && m_tStartTime != MAX_TIME) {
  351.                     if(m_tStartTime <= m_tStopTime) {
  352.                         ExecuteStart();
  353.                         ExecuteStop();
  354.                     }
  355.                     else {
  356.                         ExecuteStop();
  357.                         ExecuteStart();
  358.                     }
  359.             }
  360.             // always start off flowing when the graph starts streaming
  361.             // unless told otherwise
  362.             m_StreamState = STREAM_FLOWING;
  363.             m_FilterState = new_state;
  364.             break;
  365.  
  366.         case State_Running:
  367.  
  368.             DbgLog((LOG_TRACE,2,TEXT("Filter is RUNNING")));
  369.  
  370.             m_tRunStart = tStart;
  371.             // fall-through
  372.  
  373.         default: // case State_Paused:
  374.             m_FilterState = new_state;
  375.     }
  376.     // unblock!
  377.     m_StreamEvent.Set();
  378. }
  379.  
  380.  
  381. void CBaseStreamControl::Flushing(BOOL bInProgress) {
  382.     CAutoLock lck(&m_CritSec);
  383.     m_bIsFlushing = bInProgress;
  384.     m_StreamEvent.Set();
  385. }
  386.  
  387.